import java.io.IOException;

public class ConstructorArbolesTiny {

    public static final boolean DEBUG=false;
    
    private Token tact;
    
    private TinyGA gramaticaAtributos = new TinyGA();
    
    private AnalizadorLexicoTiny analizadorLexico;
    
    public ConstructorArbolesTiny(AnalizadorLexicoTiny analizadorLexico) {
        this.analizadorLexico = analizadorLexico;
    }
    
    public TinyGA.Sent parse() throws IOException {
        tact = analizadorLexico.sigToken();
        TinyGA.Sent aDeSent = recSent();
        rec(CatLexica.EOF);
        return aDeSent;
    }

    private TinyGA.Sent recSent() throws IOException {
        TinyGA.Decs aDeDecs = recDecs();
        rec(CatLexica.SEP);
        TinyGA.Exp0 aDeExp0 = recExp0();
        return sentR1(aDeDecs,aDeExp0);
    }
    private TinyGA.Decs recDecs() throws IOException {
        TinyGA.Dec aDeDec = recDec();
        TinyGA.Decs aDeRDecs = recRDecs(decsR2(aDeDec));
        return aDeRDecs;
    }
    private TinyGA.Decs recRDecs(TinyGA.Decs ahDeRDecs_0) throws IOException {
       if (tokenActual(CatLexica.PYCOMA)) {
          rec(CatLexica.PYCOMA);
          TinyGA.Dec aDeDec = recDec(); 
          TinyGA.Decs aDeDecs_1 = recRDecs(decsR1(ahDeRDecs_0,aDeDec));
          return aDeDecs_1;
       }
       else 
          return ahDeRDecs_0;
    }
    private TinyGA.Dec recDec() throws IOException {
        Token tDeId = tact;
        rec(CatLexica.ID);
        rec(CatLexica.IGUAL);
        Token tDeNum = tact; 
        rec(CatLexica.NUM);
        return decR1(tDeId,tDeNum);
    }
    private TinyGA.Exp0 recExp0() throws IOException {
        TinyGA.Exp1 aDeExp1 = recExp1();
        TinyGA.Exp0 aDeRExp0 = recRExp0(aDeExp1);
        TinyGA.Exp0 aDeRExp0_p = recRExp0_p(aDeRExp0);
        return aDeRExp0_p;
    } 
    private TinyGA.Exp0 recRExp0(TinyGA.Exp1 ahDeRExp0) throws IOException {
        if (tokenActual(CatLexica.MUL)) {
            rec(CatLexica.MUL);
            TinyGA.Exp1 aDeExp1 = recExp1();
            return exp0R2(ahDeRExp0,aDeExp1);     
        }
        else 
            return exp0R3(ahDeRExp0);
    }
    private TinyGA.Exp0 recRExp0_p(TinyGA.Exp0 ahDeRExp0_p_0) throws IOException {
        if (tokenActual(CatLexica.SUMA)) {
            rec(CatLexica.SUMA);
            TinyGA.Exp1 aDeExp1 = recExp1();
            TinyGA.Exp0 aDeExp0_p_1 = recRExp0_p(exp0R1(ahDeRExp0_p_0,aDeExp1));
            return aDeExp0_p_1;
        }
        else 
            return ahDeRExp0_p_0;
    }
    private TinyGA.Exp1 recExp1() throws IOException {
       if (tokenActual(CatLexica.NUM)) {
           Token tDeNum=tact;
           rec(CatLexica.NUM);
           return exp1R1(tDeNum);
       }
       else if (tokenActual(CatLexica.ID)) {
           Token tDeId=tact;
           rec(CatLexica.ID);
           return exp1R2(tDeId);
       }
       else if (tokenActual(CatLexica.PAP)) {
          rec(CatLexica.PAP);
          TinyGA.Exp0 aDeExp0 = recExp0();
          rec(CatLexica.PCIERRE);
          return exp1R3(aDeExp0);
       }
       else {
          errorSintactico(CatLexica.NUM, CatLexica.ID, CatLexica.PAP);
          return null;
       }    
    } 

    private void rec(CatLexica cat) throws IOException {
        if (tokenActual(cat)) {
            tact = sigToken();
        } else {
            errorSintactico(cat);
        }
    }

    private void errorSintactico(CatLexica... catsEsperadas) {
        System.err.print("(" + tact.fila() + "," + tact.col() + ")" 
                +"ERROR SINTACTICO: Encontrado " + tact + 
                ". Se esperaba alguno de los siguientes elementos: ");
        for (CatLexica catEsperada : catsEsperadas) {
            System.err.print(catEsperada + " ");
        }
        System.err.println();
        System.exit(1);
    }
    
    private boolean tokenActual(CatLexica ... cats) {
        boolean encontrada = false;
        int i=0;
        while (i < cats.length && ! encontrada) {
           encontrada = tact.leeCategoria() == cats[i];
           i++;
        }
        return encontrada;
    }
    
    private Token sigToken() throws IOException {
       return analizadorLexico.sigToken();
    }
    
    private TinyGA.Sent sentR1(TinyGA.Decs decs, TinyGA.Exp0 exp0) {
       if (DEBUG) return  gramaticaAtributos.new SentR1Debug(decs,exp0);
       else return gramaticaAtributos.new SentR1(decs,exp0);
    }
    private TinyGA.Decs decsR1(TinyGA.Decs decs, TinyGA.Dec dec) {
        if (DEBUG) return gramaticaAtributos.new DecsR1Debug(decs,dec);
        else return gramaticaAtributos.new DecsR1(decs,dec);
    }
    private TinyGA.Decs decsR2(TinyGA.Dec dec) {
        if (DEBUG) return gramaticaAtributos.new DecsR2Debug(dec);
        else return gramaticaAtributos.new DecsR2(dec);
    }
    private TinyGA.Dec decR1(Token id,Token num) {
       if (DEBUG) return gramaticaAtributos.new DecR1Debug(id,num);
       else return gramaticaAtributos.new DecR1(id,num);
    }
    private TinyGA.Exp0 exp0R1(TinyGA.Exp0 exp0,TinyGA.Exp1 exp1) {
       if (DEBUG) return gramaticaAtributos.new Exp0R1Debug(exp0,exp1);
       else return gramaticaAtributos.new Exp0R1(exp0,exp1);
    }
    private TinyGA.Exp0 exp0R2(TinyGA.Exp1 exp1_0,TinyGA.Exp1 exp1_1) {
        if (DEBUG) return gramaticaAtributos.new Exp0R2Debug(exp1_0,exp1_1);
        else return gramaticaAtributos.new Exp0R2(exp1_0,exp1_1);
    }
    private TinyGA.Exp0 exp0R3(TinyGA.Exp1 exp1) {
       if (DEBUG) return gramaticaAtributos.new Exp0R3Debug(exp1); 
       else return gramaticaAtributos.new Exp0R3(exp1);
    }
    private TinyGA.Exp1 exp1R1(Token num) {
        if (DEBUG) return gramaticaAtributos.new Exp1R1Debug(num);
        else return gramaticaAtributos.new Exp1R1(num);
    }
    private TinyGA.Exp1 exp1R2(Token id) {
        if (DEBUG) return gramaticaAtributos.new Exp1R2Debug(id);
        else return gramaticaAtributos.new Exp1R2(id);
    }
    private TinyGA.Exp1 exp1R3(TinyGA.Exp0 exp0) {
       if (DEBUG) return  gramaticaAtributos.new Exp1R3Debug(exp0);
       else return gramaticaAtributos.new Exp1R3(exp0);
    }
}
